#include "reader.h"
#include "log_utils.h"
#include "file_utils.h"
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#define _DEBUG_

#define LOG_TAG "AppProcessRW"

bool ELF::Reader::parseELF() {

    // 解析Elf_Ehdr
    u8 off = (u8)buff;
    ehdr = (Elf32_Ehdr*)off;

    // 解析Elf_Shdr
    shdrs = (Elf32_Shdr*)(off + ehdr->e_shoff);

    // 解析Dynamic Section
    for (u2 i = 0; i < ehdr->e_shnum; i++) {
        if(shdrs[i].sh_type == SHT_DYNAMIC) {
            dyn = (Elf32_Dyn*)(off + shdrs[i].sh_offset);
            dynSize = shdrs[i].sh_size / shdrs[i].sh_entsize;
        }
    }

    // 解析Dynamic Strtab
    for (u4 i = 0; i < dynSize; i++) {
        if (dyn[i].d_tag == DT_STRTAB) {
            strtab = reinterpret_cast<const char*>(off + dyn[i].d_un.d_ptr);
        }
    }

    // 调试状态时，可以查看解析结果是否正确
    #if defined(_DEBUG_)
    /*
    AKLog("Ehdr:\n");
    AKLog("Magic Number: 0x%02x%c%c%c\n", ehdr->e_ident[0], ehdr->e_ident[1],
            ehdr->e_ident[2], ehdr->e_ident[3]);
    char* str;
    switch(ehdr->e_ident[4]) {
        case ELFCLASS32:
            str = "32";
            break;
        case ELFCLASS64:
            str = "64";
            break;
        default:
            str = "unknown";
    }
    AKLog("Class: %s\n", class);
    switch(ehdr->e_ident[5]) {
        case ELFDATA2LSB:
            str = "Two's complement, little-endian";
            break;
        case ELFDATA2MSB:
            str = "Two's complement, big-endian.";
            break;
        default:
            str = "unknown";
    }
    AKLog("Complement: %s\n", str);
    */

    
    AKLog("Dyn:\n");
    for(u4 i = 0; i < dynSize; i++) {
        if (dyn[i].d_tag == DT_NEEDED) {
            //AKLog("[NEEDED]: %s\n", strtab + dyn[i].d_un.d_val);
            AKLog("[NEEDED]: %s\n", strtab + dyn[i].d_un.d_val);
        }
    }
    
    #endif

    return true;
}

bool ELF::Reader::replaceDebugAndRet(char** strAddr) {
    Elf32_Sword val = 0;
    bool hit = false;
    for (u4 i = 0; i < dynSize; i++) {
        if (dyn[i].d_tag == DT_NEEDED) {
            val = dyn[i].d_un.d_val + 3/*lib三个字符*/;
            hit = true;
            break;
        }
    }

    if (hit) {
        hit = false;
        for (u4 i = 0; i < dynSize; i++) {
            if (dyn[i].d_tag == DT_DEBUG) {
                dyn[i].d_tag = DT_NEEDED;
                dyn[i].d_un.d_val = val;
                hit = true;
                break;
            }
        }
    }
    *strAddr = (char*)(strtab + val);
    return hit;
}

ELF::Reader::~Reader() {
    if (fd != -1) {
        close(fd);
    }

    if (buff != NULL) {
        free(buff);
    }
}

void* ELF::Reader::getBuff() {
    return buff;
}

u8 ELF::Reader::getBuffSize() {
    return buffSize;
}

int ELF::changeSo(char* src, char* dest) {
    char* str;
    char* ap_buf= nullptr;
    file_size ap_size=read_file(src,ap_buf);
    ELF::Reader elfReader(ap_buf,ap_size);
    if (!elfReader.parseELF() || !elfReader.replaceDebugAndRet(&str)) {
        AKLogErr("parse elf file %s failed!\n", src);
        return -1;
    }

    int destfd = -1;
    destfd = open(dest, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRWXU);
    if (destfd == -1) {
        AKLogErr("can't open file %s: %s\n", dest, strerror(errno));
        return -1;
    }

    if (-1 == write(destfd, elfReader.getBuff(), elfReader.getBuffSize())) {
        AKLogErr("write file %s: %s\n", dest, strerror(errno));
        close(destfd);
        return -1;
    }

    AKLog("add a so: %s\n", str);
    close(destfd);
    return 0;
}